/*
 * @Author: lizhil
 * @Date: 2020-08-20 10:35:32
 * @Last Modified by: lizhil
 * @Last Modified time: 2021-12-14 00:30:00
 */
const moment = require('moment')
const async = require('async')
const crypto = require('crypto')
const ajv = require('./ajv')

/**
 * 字符串转驼峰
 */
function tranformStr(str) {
  var strArr = str.split('-')
  for (var i = 1; i < strArr.length; i++) {
    strArr[i] = strArr[i].charAt(0).toUpperCase() + strArr[i].substring(1)
  }
  return strArr.join('')
}

/**
 * 首字母大写
 */
function firstUpperCase(str) {
  return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase())
}

/**
 * 随机字符串
 * @param {String} length 字符串长度
 */
function nonceStr(length = 6) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  const maxPos = chars.length
  let noceStr = ''
  for (let i = 0; i < (length || 32); i++) {
    noceStr += chars.charAt(Math.floor(Math.random() * maxPos))
  }
  return noceStr
}

/**
 * 订单号
 */
function getOrderNo(min = 2, max = 8) {
  return moment().format('YYMMDD') + Math.random().toString().substr(min, max)
}

/**
 * 获得文件后缀
 * @param {String} fileName
 */
function getSuffix(fileName) {
  return fileName.split('.').pop()
}

/**
 * 获得文件名
 * @param {String} fileName
 */
function getFileName(fileName) {
  return moment().format('HHmmss') + Math.random().toString().substr(2, 5) + getSuffix(fileName)
}

/**
 * 数据格式化
 * @param {String} fileName
 */
async function dataFormat(data, options) {
  let isArray = Array.isArray(data) ? 1 : 0
  if (!isArray) data = [data]

  for (let i = 0; i < data.length; i++) {
    if (options.handle) {
      let ret = await options.handle(data[i])
      if (ret) data[i] = ret
    }

    if (options.include) data[i] = include(data[i], options.include)
    if (options.exclude) data[i] = exclude(data[i], options.exclude)
  }

  return isArray ? data : data[0]
}

/**
 * 提取对象数据
 */
function include(data, keys) {
  keys = Array.isArray(keys) ? keys : keys.split(',')

  let newData = {}

  keys.map((key) => {
    if (data[key] != undefined) newData[key] = data[key]
  })

  return newData
}

/**
 * 数据过滤
 * @param {*} data
 * @param {*} keys
 */
function exclude(data, keys) {
  keys = Array.isArray(keys) ? keys : keys.split(',')

  keys.map((key) => {
    delete data[key]
  })

  return data
}

/**
 * 数据验证
 */
function validate(data, options = {}) {
  const { required } = options

  let newData = {}

  if (required) {
    ajv(data, {
      required: required,
    })
  }

  // 排除
  if (options.exclude) {
    newData = exclude(data, options.exclude)
  }

  // 包含/提取
  if (options.include) {
    newData = include(data, options.include)
  } else {
    newData = data
  }

  return newData
}

function asyncAuto(task = {}) {
  return new Promise((resolve) => {
    async.auto(task, (err, results) => {
      resolve(results)
    })
  })
}

/**
 * 驼峰转换下划线
 * @param {*} str
 */
function toLowerLine(str) {
  var temp = str.replace(/[A-Z]/g, function (match) {
    return '_' + match.toLowerCase()
  })
  if (temp.slice(0, 1) === '_') {
    //如果首字母是大写，执行replace时会多一个_，这里需要去掉
    temp = temp.slice(1)
  }
  return temp
}

/**
 * 下横线转驼峰式
 * @param {*} str
 */
function toCamel(str) {
  return str.replace(/([^_])(?:_+([^_]))/g, function ($0, $1, $2) {
    return $1 + $2.toUpperCase()
  })
}

/**
 * 路由匹配(url没有被拦截或过滤时)
 * @param {*} ignore
 * @param {*} intercept
 * @param {*} url
 */
function routerMatch(url, urls, ignore) {
  // 判断是否过滤当前URL匹配
  if (ignore && ignore.length) {
    const ret = urlMatch(ignore, url)

    // 匹配失败
    if (ret) {
      console.log('过滤路由')
      return false
    }
  }

  // 判断URL是否匹配
  if (urls && urls.length) return urlMatch(urls, url)
}

/**
 * URL匹配
 */
function urlMatch(urls, url) {
  // 过滤
  if (urls && urls.length) {
    const ret = urls.some((v) => {
      return url.indexOf(v) == 0
    })

    return ret ? true : false
  }
}

function randomStr(length = 32) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  const maxPos = chars.length
  let noceStr = ''
  for (let i = 0; i < (length || 32); i++) {
    noceStr += chars.charAt(Math.floor(Math.random() * maxPos))
  }
  return noceStr
}

function md5(str) {
  str = str + ''
  let crypto_md5 = crypto.createHash('md5')
  crypto_md5.update(str, 'utf8') // 编码
  return crypto_md5.digest('hex')
}

// 获取2个值之间的随机数
function getRandom(min, max) {
  let Range = max - min
  let Rand = Math.random()
  return min + Math.round(Rand * Range)
}

/**
 * 将文本转换成Mongo正则
 * @param {String} 文本
 * @returns
 */
function toMongoRegex(str = '') {
  return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
}

/**
 * 转换为前端所需价格
 */
function toPriceDesc(data, keys, length = 2) {
  keys.forEach((key) => {
    if (data[key] == undefined) return
    data[`${key}Desc`] = data[key].toFixed(length)
  })
}

/**
 * 网络图片url转base64
 */
async function getBase64ByUrl(url) {
  const fs = require('fs')
  const download = require('download')
  // 文件保存路径
  let path = 'dist/'
  let fileName = url.split('/').slice(-1)
  await download(url, path)
  let data = fs.readFileSync(path + fileName)
  let base64Str = new Buffer(data).toString('base64')
  fs.unlink(path + fileName, (err) => {
    if (err) {
      console.log(err)
      return false
    }
    console.log('删除文件成功')
  })

  let crypto_md5 = crypto.createHash('md5')
  crypto_md5.update(data, 'utf8') // 编码
  let md5Str = crypto_md5.digest('hex')
  return {
    base64: base64Str,
    md5: md5Str,
  }
}

/**
 * 重试
 * @param {function} fn
 * @param {Number} num 重试次数
 */
async function retry(fn, num = 1) {
  try {
    return await fn()
  } catch (e) {
    // 尝试重试
    if (num - 1 != 0) {
      return retry(fn, num - 1)
    }
    // 重试后，还是错误
    else {
      throw e
    }
  }
}

module.exports = {
  ajv,
  randomStr,
  md5,
  urlMatch,
  routerMatch,
  include,
  exclude,
  dataFormat,
  getFileName,
  getSuffix,
  getOrderNo,
  tranformStr,
  firstUpperCase,
  nonceStr,
  validate,
  asyncAuto,
  toLowerLine,
  toCamel,
  getRandom,
  toMongoRegex,
  toPriceDesc,
  getBase64ByUrl,

  toFloat: function (number, fractionDigits = 2) {
    const value = number.toFixed(3)
    return Number(value.substring(0, value.length - 1))
  },

  /**
   * 异步队列
   * @param {*} data 队列
   * @param {*} exec 执行函数
   * @param {*} concurrency 同时并发数
   */
  queue: async function (data, exec, concurrency = 50) {
    const newData = []

    if (typeof data == 'number') {
      data = new Array(data).fill('')
    }

    if (data.length == 0) {
      return data
    }

    // 定义一个队列
    const q = async.queue(async (task) => {
      const newItem = await exec(task.item, task.index)

      newData[task.index] = newItem
    }, concurrency)

    // 队列完成
    q.drain(function () {
      return newData
    })

    // 队列异常
    q.error(function (err, task) {
      console.error('队列异常', exec, err)
    })

    // 创建队列
    data.forEach((item, index) => {
      q.push(
        {
          item,
          index,
        },
        function () {
          // console.log('队列', index)
        }
      )
    })

    await q.drain()

    return newData
  },

  retry,
}
